package sharewithus.analyzer.tag;

import java.util.List;

import sharewithus.analyzer.POSInf;
import sharewithus.analyzer.PartOfSpeech;
import sharewithus.analyzer.seg.SUToken;


/**
 * 词性标注器
 * @author terry
 *
 */
public class Tagger {
	
	private static final POSContextState posContextState = POSContextState.getInstance();
	
	/**
	 * 标注估计词性(隐马尔科夫模型,viterbi算法)
	 * 
	 * @param ret token序列
	 * 
	 */
	public static void hmm(List<SUToken> ret){
		ret.add(0, SUToken.START_TOKEN);
		ret.add(SUToken.END_TOKEN);

		// 初始化各个节点的隐状态,每个节点都有PartOfSpeech.values().length种隐状态
		// 形成了一个row=stageLength,line=PartOfSpeech.values().length的二维数组
		int stageLength = ret.size();
		double[][] probs = new double[stageLength][PartOfSpeech.values().length];
		for (int i = 0; i < stageLength; i++)
			for (int j = 0; j < PartOfSpeech.values().length; j++)
				probs[i][j] = Double.NEGATIVE_INFINITY;

		// 在隐马尔科夫模型中,每个隐状态都有一个最佳前驱,此二维数组用来储存每个状态的最佳前驱
		PartOfSpeech[][] bestPre = new PartOfSpeech[stageLength][PartOfSpeech
				.values().length];
		for (int i = 0; i < stageLength; ++i)
			for (int j = 0; j < PartOfSpeech.values().length; ++j)
				bestPre[i][j] = PartOfSpeech.UNKNOW;

		probs[0][PartOfSpeech.START.ordinal()] = 1;

		for (int stage = 1; stage < stageLength; stage++) {
			SUToken currToken = ret.get(stage);
			SUToken prevToken = ret.get(stage - 1);
			if (currToken.getData().isEmpty() || prevToken.getData().isEmpty())
				continue;
			for (POSInf currPosInf : currToken.getData()) {// 遍历当前词的每个词性
				// 求出发射概率 = 当前词作为当前词性出现的频率 / 当前词性在语料库中出现的频率
				double emiprob = posContextState.getEmiprob(
						currPosInf.getPos(), currPosInf.getFreq());

				for (POSInf prevPosInf : prevToken.getData()) {// 遍历上一个词的每个词性
					double transprob = posContextState.getTransProb(prevPosInf
							.getPos(), currPosInf.getPos()); // 求出上个词的词性转移到当前词性的概率的对数
					double preProb = probs[stage - 1][prevPosInf.getPos()
							.ordinal()]; // 前驱最佳概率
					// log(前驱最佳概率) + log(发射概率) + log(转移概率)
					double currentProb = preProb + transprob + emiprob;
					
					if (probs[stage][currPosInf.getPos().ordinal()] <= currentProb) { // 计算最佳前驱
						probs[stage][currPosInf.getPos().ordinal()] = currentProb;
						bestPre[stage][currPosInf.getPos().ordinal()] = prevPosInf
								.getPos();
					}
				}
			}
		}

		PartOfSpeech tmpTag = PartOfSpeech.END;
		for (int i = stageLength - 1; i > 1; i--) {
			PartOfSpeech prevTag = bestPre[i][tmpTag.ordinal()];
			ret.get(i - 1).setTaggedPOS(prevTag) ;
			tmpTag = prevTag;
		}

		ret.remove(stageLength - 1);
		ret.remove(0);
	}
}
